home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / lib / iceweasel / components / FeedConverter.js < prev    next >
Encoding:
Text File  |  2013-01-09  |  18.5 KB  |  580 lines

  1. //@line 39 "/tmp/buildd/iceweasel-10.0.12esr/browser/components/feeds/src/FeedConverter.js"
  2.  
  3. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  4. Components.utils.import("resource://gre/modules/debug.js");
  5.  
  6. const Cc = Components.classes;
  7. const Ci = Components.interfaces;
  8. const Cr = Components.results;
  9.  
  10. function LOG(str) {
  11.   dump("*** " + str + "\n");
  12. }
  13.  
  14. const FS_CONTRACTID = "@mozilla.org/browser/feeds/result-service;1";
  15. const FPH_CONTRACTID = "@mozilla.org/network/protocol;1?name=feed";
  16. const PCPH_CONTRACTID = "@mozilla.org/network/protocol;1?name=pcast";
  17.  
  18. const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
  19. const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed";
  20. const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed";
  21. const TYPE_ANY = "*/*";
  22.  
  23. const FEEDHANDLER_URI = "about:feeds";
  24.  
  25. const PREF_SELECTED_APP = "browser.feeds.handlers.application";
  26. const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
  27. const PREF_SELECTED_ACTION = "browser.feeds.handler";
  28. const PREF_SELECTED_READER = "browser.feeds.handler.default";
  29.  
  30. const PREF_VIDEO_SELECTED_APP = "browser.videoFeeds.handlers.application";
  31. const PREF_VIDEO_SELECTED_WEB = "browser.videoFeeds.handlers.webservice";
  32. const PREF_VIDEO_SELECTED_ACTION = "browser.videoFeeds.handler";
  33. const PREF_VIDEO_SELECTED_READER = "browser.videoFeeds.handler.default";
  34.  
  35. const PREF_AUDIO_SELECTED_APP = "browser.audioFeeds.handlers.application";
  36. const PREF_AUDIO_SELECTED_WEB = "browser.audioFeeds.handlers.webservice";
  37. const PREF_AUDIO_SELECTED_ACTION = "browser.audioFeeds.handler";
  38. const PREF_AUDIO_SELECTED_READER = "browser.audioFeeds.handler.default";
  39.  
  40. function getPrefAppForType(t) {
  41.   switch (t) {
  42.     case Ci.nsIFeed.TYPE_VIDEO:
  43.       return PREF_VIDEO_SELECTED_APP;
  44.  
  45.     case Ci.nsIFeed.TYPE_AUDIO:
  46.       return PREF_AUDIO_SELECTED_APP;
  47.  
  48.     default:
  49.       return PREF_SELECTED_APP;
  50.   }
  51. }
  52.  
  53. function getPrefWebForType(t) {
  54.   switch (t) {
  55.     case Ci.nsIFeed.TYPE_VIDEO:
  56.       return PREF_VIDEO_SELECTED_WEB;
  57.  
  58.     case Ci.nsIFeed.TYPE_AUDIO:
  59.       return PREF_AUDIO_SELECTED_WEB;
  60.  
  61.     default:
  62.       return PREF_SELECTED_WEB;
  63.   }
  64. }
  65.  
  66. function getPrefActionForType(t) {
  67.   switch (t) {
  68.     case Ci.nsIFeed.TYPE_VIDEO:
  69.       return PREF_VIDEO_SELECTED_ACTION;
  70.  
  71.     case Ci.nsIFeed.TYPE_AUDIO:
  72.       return PREF_AUDIO_SELECTED_ACTION;
  73.  
  74.     default:
  75.       return PREF_SELECTED_ACTION;
  76.   }
  77. }
  78.  
  79. function getPrefReaderForType(t) {
  80.   switch (t) {
  81.     case Ci.nsIFeed.TYPE_VIDEO:
  82.       return PREF_VIDEO_SELECTED_READER;
  83.  
  84.     case Ci.nsIFeed.TYPE_AUDIO:
  85.       return PREF_AUDIO_SELECTED_READER;
  86.  
  87.     default:
  88.       return PREF_SELECTED_READER;
  89.   }
  90. }
  91.  
  92. function safeGetCharPref(pref, defaultValue) {
  93.   var prefs =   
  94.       Cc["@mozilla.org/preferences-service;1"].
  95.       getService(Ci.nsIPrefBranch);
  96.   try {
  97.     return prefs.getCharPref(pref);
  98.   }
  99.   catch (e) {
  100.   }
  101.   return defaultValue;
  102. }
  103.  
  104. function FeedConverter() {
  105. }
  106. FeedConverter.prototype = {
  107.   classID: Components.ID("{229fa115-9412-4d32-baf3-2fc407f76fb1}"),
  108.  
  109.   /**
  110.    * This is the downloaded text data for the feed.
  111.    */
  112.   _data: null,
  113.   
  114.   /**
  115.    * This is the object listening to the conversion, which is ultimately the
  116.    * docshell for the load.
  117.    */
  118.   _listener: null,
  119.  
  120.   /**
  121.    * Records if the feed was sniffed
  122.    */
  123.   _sniffed: false,
  124.  
  125.   /**
  126.    * See nsIStreamConverter.idl
  127.    */
  128.   convert: function FC_convert(sourceStream, sourceType, destinationType, 
  129.                                context) {
  130.     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  131.   },
  132.   
  133.   /**
  134.    * See nsIStreamConverter.idl
  135.    */
  136.   asyncConvertData: function FC_asyncConvertData(sourceType, destinationType,
  137.                                                  listener, context) {
  138.     this._listener = listener;
  139.   },
  140.   
  141.   /**
  142.    * Whether or not the preview page is being forced.
  143.    */
  144.   _forcePreviewPage: false,
  145.   
  146.   /** 
  147.    * Release our references to various things once we're done using them.
  148.    */
  149.   _releaseHandles: function FC__releaseHandles() {
  150.     this._listener = null;
  151.     this._request = null;
  152.     this._processor = null;
  153.   },
  154.   
  155.   /**
  156.    * See nsIFeedResultListener.idl
  157.    */
  158.   handleResult: function FC_handleResult(result) {
  159.     // Feeds come in various content types, which our feed sniffer coerces to
  160.     // the maybe.feed type. However, feeds are used as a transport for 
  161.     // different data types, e.g. news/blogs (traditional feed), video/audio
  162.     // (podcasts) and photos (photocasts, photostreams). Each of these is 
  163.     // different in that there's a different class of application suitable for
  164.     // handling feeds of that type, but without a content-type differentiation
  165.     // it is difficult for us to disambiguate.
  166.     // 
  167.     // The other problem is that if the user specifies an auto-action handler
  168.     // for one feed application, the fact that the content type is shared means 
  169.     // that all other applications will auto-load with that handler too, 
  170.     // regardless of the content-type. 
  171.     //
  172.     // This means that content-type alone is not enough to determine whether
  173.     // or not a feed should be auto-handled. This means that for feeds we need
  174.     // to always use this stream converter, even when an auto-action is 
  175.     // specified, not the basic one provided by WebContentConverter. This 
  176.     // converter needs to consume all of the data and parse it, and based on
  177.     // that determination make a judgment about type. 
  178.     //
  179.     // Since there are no content types for this content, and I'm not going to
  180.     // invent any, the upshot is that while a user can set an auto-handler for
  181.     // generic feed content, the system will prevent them from setting an auto-
  182.     // handler for other stream types. In those cases, the user will always see
  183.     // the preview page and have to select a handler. We can guess and show 
  184.     // a client handler, but will not be able to show web handlers for those
  185.     // types.
  186.     //
  187.     // If this is just a feed, not some kind of specialized application, then
  188.     // auto-handlers can be set and we should obey them. 
  189.     try {
  190.       var feedService = 
  191.           Cc["@mozilla.org/browser/feeds/result-service;1"].
  192.           getService(Ci.nsIFeedResultService);
  193.       if (!this._forcePreviewPage && result.doc) {
  194.         var feed = result.doc.QueryInterface(Ci.nsIFeed);
  195.         var handler = safeGetCharPref(getPrefActionForType(feed.type), "ask");
  196.  
  197.         if (handler != "ask") {
  198.           if (handler == "reader")
  199.             handler = safeGetCharPref(getPrefReaderForType(feed.type), "bookmarks");
  200.           switch (handler) {
  201.             case "web":
  202.               var wccr = 
  203.                   Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
  204.                   getService(Ci.nsIWebContentConverterService);
  205.               if ((feed.type == Ci.nsIFeed.TYPE_FEED &&
  206.                    wccr.getAutoHandler(TYPE_MAYBE_FEED)) ||
  207.                   (feed.type == Ci.nsIFeed.TYPE_VIDEO &&
  208.                    wccr.getAutoHandler(TYPE_MAYBE_VIDEO_FEED)) ||
  209.                   (feed.type == Ci.nsIFeed.TYPE_AUDIO &&
  210.                    wccr.getAutoHandler(TYPE_MAYBE_AUDIO_FEED))) {
  211.                 wccr.loadPreferredHandler(this._request);
  212.                 return;
  213.               }
  214.               break;
  215.  
  216.             default:
  217.               LOG("unexpected handler: " + handler);
  218.               // fall through -- let feed service handle error
  219.             case "bookmarks":
  220.             case "client":
  221.               try {
  222.                 var title = feed.title ? feed.title.plainText() : "";
  223.                 var desc = feed.subtitle ? feed.subtitle.plainText() : "";
  224.                 feedService.addToClientReader(result.uri.spec, title, desc, feed.type);
  225.                 return;
  226.               } catch(ex) { /* fallback to preview mode */ }
  227.           }
  228.         }
  229.       }
  230.           
  231.       var ios = 
  232.           Cc["@mozilla.org/network/io-service;1"].
  233.           getService(Ci.nsIIOService);
  234.       var chromeChannel;
  235.  
  236.       // If there was no automatic handler, or this was a podcast,
  237.       // photostream or some other kind of application, show the preview page
  238.       // if the parser returned a document.
  239.       if (result.doc) {
  240.  
  241.         // Store the result in the result service so that the display
  242.         // page can access it.
  243.         feedService.addFeedResult(result);
  244.  
  245.         // Now load the actual XUL document.
  246.         var chromeURI = ios.newURI(FEEDHANDLER_URI, null, null);
  247.         chromeChannel = ios.newChannelFromURI(chromeURI, null);
  248.         chromeChannel.originalURI = result.uri;
  249.       }
  250.       else
  251.         chromeChannel = ios.newChannelFromURI(result.uri, null);
  252.  
  253.       chromeChannel.loadGroup = this._request.loadGroup;
  254.       chromeChannel.asyncOpen(this._listener, null);
  255.     }
  256.     finally {
  257.       this._releaseHandles();
  258.     }
  259.   },
  260.   
  261.   /**
  262.    * See nsIStreamListener.idl
  263.    */
  264.   onDataAvailable: function FC_onDataAvailable(request, context, inputStream, 
  265.                                                sourceOffset, count) {
  266.     if (this._processor)
  267.       this._processor.onDataAvailable(request, context, inputStream,
  268.                                       sourceOffset, count);
  269.   },
  270.   
  271.   /**
  272.    * See nsIRequestObserver.idl
  273.    */
  274.   onStartRequest: function FC_onStartRequest(request, context) {
  275.     var channel = request.QueryInterface(Ci.nsIChannel);
  276.  
  277.     // Check for a header that tells us there was no sniffing
  278.     // The value doesn't matter.
  279.     try {
  280.       var httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
  281.       // Make sure to check requestSucceeded before the potentially-throwing
  282.       // getResponseHeader.
  283.       if (!httpChannel.requestSucceeded) {
  284.         // Just give up, but don't forget to cancel the channel first!
  285.         request.cancel(0x804b0002); // NS_BINDING_ABORTED
  286.         return;
  287.       }
  288.       var noSniff = httpChannel.getResponseHeader("X-Moz-Is-Feed");
  289.     }
  290.     catch (ex) {
  291.       this._sniffed = true;
  292.     }
  293.  
  294.     this._request = request;
  295.     
  296.     // Save and reset the forced state bit early, in case there's some kind of
  297.     // error.
  298.     var feedService = 
  299.         Cc["@mozilla.org/browser/feeds/result-service;1"].
  300.         getService(Ci.nsIFeedResultService);
  301.     this._forcePreviewPage = feedService.forcePreviewPage;
  302.     feedService.forcePreviewPage = false;
  303.  
  304.     // Parse feed data as it comes in
  305.     this._processor =
  306.         Cc["@mozilla.org/feed-processor;1"].
  307.         createInstance(Ci.nsIFeedProcessor);
  308.     this._processor.listener = this;
  309.     this._processor.parseAsync(null, channel.URI);
  310.     
  311.     this._processor.onStartRequest(request, context);
  312.   },
  313.   
  314.   /**
  315.    * See nsIRequestObserver.idl
  316.    */
  317.   onStopRequest: function FC_onStopRequest(request, context, status) {
  318.     if (this._processor)
  319.       this._processor.onStopRequest(request, context, status);
  320.   },
  321.   
  322.   /**
  323.    * See nsISupports.idl
  324.    */
  325.   QueryInterface: function FC_QueryInterface(iid) {
  326.     if (iid.equals(Ci.nsIFeedResultListener) ||
  327.         iid.equals(Ci.nsIStreamConverter) ||
  328.         iid.equals(Ci.nsIStreamListener) ||
  329.         iid.equals(Ci.nsIRequestObserver)||
  330.         iid.equals(Ci.nsISupports))
  331.       return this;
  332.     throw Cr.NS_ERROR_NO_INTERFACE;
  333.   },
  334. };
  335.  
  336. /**
  337.  * Keeps parsed FeedResults around for use elsewhere in the UI after the stream
  338.  * converter completes. 
  339.  */
  340. function FeedResultService() {
  341. }
  342.  
  343. FeedResultService.prototype = {
  344.   classID: Components.ID("{2376201c-bbc6-472f-9b62-7548040a61c6}"),
  345.   
  346.   /**
  347.    * A URI spec -> [nsIFeedResult] hash. We have to keep a list as the
  348.    * value in case the same URI is requested concurrently.
  349.    */
  350.   _results: { },
  351.   
  352.   /**
  353.    * See nsIFeedResultService.idl
  354.    */
  355.   forcePreviewPage: false,
  356.   
  357.   /**
  358.    * See nsIFeedResultService.idl
  359.    */
  360.   addToClientReader: function FRS_addToClientReader(spec, title, subtitle, feedType) {
  361.     var prefs =   
  362.         Cc["@mozilla.org/preferences-service;1"].
  363.         getService(Ci.nsIPrefBranch);
  364.  
  365.     var handler = safeGetCharPref(getPrefActionForType(feedType), "bookmarks");
  366.     if (handler == "ask" || handler == "reader")
  367.       handler = safeGetCharPref(getPrefReaderForType(feedType), "bookmarks");
  368.  
  369.     switch (handler) {
  370.     case "client":
  371.       var clientApp = prefs.getComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile);
  372.  
  373.       // For the benefit of applications that might know how to deal with more
  374.       // URLs than just feeds, send feed: URLs in the following format:
  375.       //
  376.       // http urls: replace scheme with feed, e.g.
  377.       // http://foo.com/index.rdf -> feed://foo.com/index.rdf
  378.       // other urls: prepend feed: scheme, e.g.
  379.       // https://foo.com/index.rdf -> feed:https://foo.com/index.rdf
  380.       var ios = 
  381.           Cc["@mozilla.org/network/io-service;1"].
  382.           getService(Ci.nsIIOService);
  383.       var feedURI = ios.newURI(spec, null, null);
  384.       if (feedURI.schemeIs("http")) {
  385.         feedURI.scheme = "feed";
  386.         spec = feedURI.spec;
  387.       }
  388.       else
  389.         spec = "feed:" + spec;
  390.  
  391.       // Retrieving the shell service might fail on some systems, most
  392.       // notably systems where GNOME is not installed.
  393.       try {
  394.         var ss =
  395.             Cc["@mozilla.org/browser/shell-service;1"].
  396.             getService(Ci.nsIShellService);
  397.         ss.openApplicationWithURI(clientApp, spec);
  398.       } catch(e) {
  399.         // If we couldn't use the shell service, fallback to using a
  400.         // nsIProcess instance
  401.         var p =
  402.             Cc["@mozilla.org/process/util;1"].
  403.             createInstance(Ci.nsIProcess);
  404.         p.init(clientApp);
  405.         p.run(false, [spec], 1);
  406.       }
  407.       break;
  408.  
  409.     default:
  410.       // "web" should have been handled elsewhere
  411.       LOG("unexpected handler: " + handler);
  412.       // fall through
  413.     case "bookmarks":
  414.       var wm = 
  415.           Cc["@mozilla.org/appshell/window-mediator;1"].
  416.           getService(Ci.nsIWindowMediator);
  417.       var topWindow = wm.getMostRecentWindow("navigator:browser");
  418.       topWindow.PlacesCommandHook.addLiveBookmark(spec, title, subtitle);
  419.       break;
  420.     }
  421.   },
  422.   
  423.   /**
  424.    * See nsIFeedResultService.idl
  425.    */
  426.   addFeedResult: function FRS_addFeedResult(feedResult) {
  427.     NS_ASSERT(feedResult.uri != null, "null URI!");
  428.     NS_ASSERT(feedResult.uri != null, "null feedResult!");
  429.     var spec = feedResult.uri.spec;
  430.     if(!this._results[spec])  
  431.       this._results[spec] = [];
  432.     this._results[spec].push(feedResult);
  433.   },
  434.   
  435.   /**
  436.    * See nsIFeedResultService.idl
  437.    */
  438.   getFeedResult: function RFS_getFeedResult(uri) {
  439.     NS_ASSERT(uri != null, "null URI!");
  440.     var resultList = this._results[uri.spec];
  441.     for (var i in resultList) {
  442.       if (resultList[i].uri == uri)
  443.         return resultList[i];
  444.     }
  445.     return null;
  446.   },
  447.   
  448.   /**
  449.    * See nsIFeedResultService.idl
  450.    */
  451.   removeFeedResult: function FRS_removeFeedResult(uri) {
  452.     NS_ASSERT(uri != null, "null URI!");
  453.     var resultList = this._results[uri.spec];
  454.     if (!resultList)
  455.       return;
  456.     var deletions = 0;
  457.     for (var i = 0; i < resultList.length; ++i) {
  458.       if (resultList[i].uri == uri) {
  459.         delete resultList[i];
  460.         ++deletions;
  461.       }
  462.     }
  463.     
  464.     // send the holes to the end
  465.     resultList.sort();
  466.     // and trim the list
  467.     resultList.splice(resultList.length - deletions, deletions);
  468.     if (resultList.length == 0)
  469.       delete this._results[uri.spec];
  470.   },
  471.  
  472.   createInstance: function FRS_createInstance(outer, iid) {
  473.     if (outer != null)
  474.       throw Cr.NS_ERROR_NO_AGGREGATION;
  475.     return this.QueryInterface(iid);
  476.   },
  477.   
  478.   QueryInterface: function FRS_QueryInterface(iid) {
  479.     if (iid.equals(Ci.nsIFeedResultService) ||
  480.         iid.equals(Ci.nsIFactory) ||
  481.         iid.equals(Ci.nsISupports))
  482.       return this;
  483.     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  484.   },
  485. };
  486.  
  487. /**
  488.  * A protocol handler that attempts to deal with the variant forms of feed:
  489.  * URIs that are actually either http or https.
  490.  */
  491. function GenericProtocolHandler() {
  492. }
  493. GenericProtocolHandler.prototype = {
  494.   _init: function GPH_init(scheme) {
  495.     var ios = 
  496.       Cc["@mozilla.org/network/io-service;1"].
  497.       getService(Ci.nsIIOService);
  498.     this._http = ios.getProtocolHandler("http");
  499.     this._scheme = scheme;
  500.   },
  501.  
  502.   get scheme() {
  503.     return this._scheme;
  504.   },
  505.   
  506.   get protocolFlags() {
  507.     return this._http.protocolFlags;
  508.   },
  509.   
  510.   get defaultPort() {
  511.     return this._http.defaultPort;
  512.   },
  513.   
  514.   allowPort: function GPH_allowPort(port, scheme) {
  515.     return this._http.allowPort(port, scheme);
  516.   },
  517.   
  518.   newURI: function GPH_newURI(spec, originalCharset, baseURI) {
  519.     // Feed URIs can be either nested URIs of the form feed:realURI (in which
  520.     // case we create a nested URI for the realURI) or feed://example.com, in
  521.     // which case we create a nested URI for the real protocol which is http.
  522.  
  523.     var scheme = this._scheme + ":";
  524.     if (spec.substr(0, scheme.length) != scheme)
  525.       throw Cr.NS_ERROR_MALFORMED_URI;
  526.  
  527.     var prefix = spec.substr(scheme.length, 2) == "//" ? "http:" : "";
  528.     var inner = Cc["@mozilla.org/network/io-service;1"].
  529.                 getService(Ci.nsIIOService).newURI(spec.replace(scheme, prefix),
  530.                                                    originalCharset, baseURI);
  531.     var netutil = Cc["@mozilla.org/network/util;1"].getService(Ci.nsINetUtil);
  532.     const URI_INHERITS_SECURITY_CONTEXT = Ci.nsIProtocolHandler
  533.                                             .URI_INHERITS_SECURITY_CONTEXT;
  534.     if (netutil.URIChainHasFlags(inner, URI_INHERITS_SECURITY_CONTEXT))
  535.       throw Cr.NS_ERROR_MALFORMED_URI;
  536.  
  537.     var uri = netutil.newSimpleNestedURI(inner);
  538.     uri.spec = inner.spec.replace(prefix, scheme);
  539.     return uri;
  540.   },
  541.   
  542.   newChannel: function GPH_newChannel(aUri) {
  543.     var inner = aUri.QueryInterface(Ci.nsINestedURI).innerURI;
  544.     var channel = Cc["@mozilla.org/network/io-service;1"].
  545.                   getService(Ci.nsIIOService).newChannelFromURI(inner, null);
  546.     if (channel instanceof Components.interfaces.nsIHttpChannel)
  547.       // Set this so we know this is supposed to be a feed
  548.       channel.setRequestHeader("X-Moz-Is-Feed", "1", false);
  549.     channel.originalURI = aUri;
  550.     return channel;
  551.   },
  552.   
  553.   QueryInterface: function GPH_QueryInterface(iid) {
  554.     if (iid.equals(Ci.nsIProtocolHandler) ||
  555.         iid.equals(Ci.nsISupports))
  556.       return this;
  557.     throw Cr.NS_ERROR_NO_INTERFACE;
  558.   }  
  559. };
  560.  
  561. function FeedProtocolHandler() {
  562.   this._init('feed');
  563. }
  564. FeedProtocolHandler.prototype = new GenericProtocolHandler();
  565. FeedProtocolHandler.prototype.classID = Components.ID("{4f91ef2e-57ba-472e-ab7a-b4999e42d6c0}");
  566.  
  567. function PodCastProtocolHandler() {
  568.   this._init('pcast');
  569. }
  570. PodCastProtocolHandler.prototype = new GenericProtocolHandler();
  571. PodCastProtocolHandler.prototype.classID = Components.ID("{1c31ed79-accd-4b94-b517-06e0c81999d5}");
  572.  
  573. var components = [FeedConverter,
  574.                   FeedResultService,
  575.                   FeedProtocolHandler,
  576.                   PodCastProtocolHandler];
  577.  
  578.  
  579. const NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
  580.